package code.parser;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 根据.h文件生成.java和.cs文件
 * 将c/c++语法中结构体 从语法上转换成java或c#的类
 * 
 * @author GeLiang
 *
 */
public class StructToClass {
	public static void main(String[] args) throws IOException {
		struct2class(
				new File("D:\\workspaceWeb\\c2java\\src\\MatchVSDefines.h"),
				new File("D:\\workspaceWeb\\c2java\\src\\cs\\"), ".cs","MatchVS");
		struct2class(
				new File("D:\\workspaceWeb\\c2java\\src\\MatchVSDefines.h"),
				new File("D:\\workspaceWeb\\c2java\\src\\"), ".java","matchvs");
	}

	public static void struct2class(File h_cfile, File outFileDir,
			String classFileSuffix,String packageName) throws IOException {
		String txt = txt2String(h_cfile);
		Map<String, String> defineMap = getAllDefine(h_cfile);
		defineMap.put("Int32", "int");
		if (classFileSuffix.equals(".cs")) {
			defineMap.put("MsString", "string");
			defineMap.put("namespace", "namespace %s {\n\n%s\n}");
		} else {
			defineMap.put("MsString", "String");
			defineMap.put("bool", "boolean");
			defineMap.put("namespace", "package %s;\n\n%s");
		}
		txt = replaceDefine(defineMap, txt);
		List<Struct> structList = getAllStructInfo(txt);
		// 语法转换类型.
		structList = genAllClass(structList);
		// 将类信息写入单独的文件
		for (Struct struct : structList) {
			StringBuilder sb = new StringBuilder();
			sb.append("public  class ").append(struct.name).append("{");
			String outputString = replaceArray(struct.javaSource);
			sb.append(outputString);
			sb.append("}");
			
			String targetSource = String.format(defineMap.get("namespace"), packageName,sb.toString());
			if (!outFileDir.exists()) {
				outFileDir.mkdirs();
			}
			final File file = new File(outFileDir, struct.name
					+ classFileSuffix);
			if (!file.exists()) {
				file.createNewFile();
			}
			FileOutputStream fos = new FileOutputStream(file);
			fos.write(targetSource.getBytes());
			fos.flush();
			fos.close();
		}

	}

	public static String replaceArray(String txt) {
		String re = "(\\s*)([a-zA-Z_0-9][a-zA-Z0-9_]*)(\\s+)([a-zA-Z_0-9][a-zA-Z0-9_]*)(\\[\\d+\\])"; // Any
																										// Single
																										// Character
																										// 1
		Pattern p = Pattern.compile(re, Pattern.CASE_INSENSITIVE
				| Pattern.DOTALL);
//		System.out.println(p);
		Matcher m = p.matcher(txt);
		Map<String, String> replaces = new HashMap<String, String>();
		while (m.find()) {
//			System.out.println(m.group(1));
//			System.out.println(m.group(2));
//			System.out.println(m.group(3));
//			System.out.println(m.group(4));
//			System.out.println(m.group(5));
//			System.out.println("_______________________");
			boolean isAddRoundbrackets = false;
			if (m.group(2).equals("int") || m.group(2).equals("long")
					|| m.group(2).equals("short")
					|| m.group(2).equals("String") || m.group(2).equals("char")
					|| m.group(2).equals("byte")) {
				isAddRoundbrackets = false;
			}

			replaces.put(
					m.group(1) + m.group(2) + m.group(3) + m.group(4)
							+ m.group(5), m.group(1) + m.group(2) + m.group(3)
							+ "[]" + m.group(4) + " = new " + m.group(2)
							+ (isAddRoundbrackets ? "()" : "") + m.group(5));
		}
		final Set<String> keySet = replaces.keySet();
		for (String string : keySet) {
			txt = txt.replace(string, replaces.get(string));
		}
		return txt;
	}

	public static List<Struct> genAllClass(List<Struct> structList) {
		for (Struct struct : structList) {
			String txt = struct.source;
			String re1 = "(\\{)"; // Any Single Character 1
			String re2 = "(.*?)"; // Non-greedy match on filler
			String re3 = "(" + struct.name + "\\()"; // Word 1
			String re4 = "(.*?)"; // Non-greedy match on filler
			String re5 = "(\\})"; // Any Single Character 3
			String re6 = "([\\s\\S]*)"; // White Space 2
			String re7 = "(\\})"; // Any Single Character 4
			Pattern p = Pattern.compile(
					re1 + re2 + re3 + re4 + re5 + re6 + re7,
					Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
//			System.out.println("===========" + "提取构造函数" + "==============");
			Matcher m = p.matcher(txt);
			if (m.find()) {
				// String w1=m.group(1);
				// String c1=m.group(2);
				String ws1 = m.group(3);
				String word1 = m.group(4);
				String c2 = m.group(5);
				// String c3=m.group(6);
				// String ws2=m.group(7);
				final String constructor = ws1 + word1 + c2;
				// System.out.println(constructor);
//				System.out.println("===========" + constructor
//						+ "==============");

				struct.javaSource = txt.replace(constructor, "\n");

				// System.out.println(struct.javaSource);

			} else {
				struct.javaSource = txt;
			}

			struct.javaSource = struct.javaSource.replaceAll("\\{", "");
			struct.javaSource = struct.javaSource.replaceAll("(\\})(\\s*)("
					+ struct.name + ")(\\s*)(;)", "");
			struct.javaSource = struct.javaSource.replaceAll("int\\s+", "public int ");
			struct.javaSource = struct.javaSource.replaceAll("String\\s+", "public String ");
			struct.javaSource = struct.javaSource.replaceAll("string\\s+", "public string ");
			struct.javaSource = struct.javaSource.replaceAll("byte\\s+", "public byte ");
			struct.javaSource = struct.javaSource.replaceAll("bool", "public bool");
			struct.javaSource = struct.javaSource.replaceAll("short", "public short");
			struct.javaSource = struct.javaSource.replaceAll("char", "public byte");
		}
		return structList;
	}

	private static String replaceDefine(Map<String, String> defineMap,
			String txt) {
		Set<String> set = defineMap.keySet();
		for (String define : set) {

			final String replacement = defineMap.get(define);
			txt = txt.replace(define, replacement == null ? "" : replacement);
		}
		return txt;
	}

	public static String txt2String(File file) {
		String result = "";
		try {
			BufferedReader br = new BufferedReader(new FileReader(file));// 构造一个BufferedReader类来读取文件
			String s = null;
			while ((s = br.readLine()) != null) {// 使用readLine方法，一次读一行
				result = result + "\n" + s;
			}
			br.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	private static Map<String, String> getAllDefine(File hfile)
			throws IOException {
		BufferedReader fis = new BufferedReader(new FileReader(hfile));
		HashMap<String, String> map = new HashMap<String, String>();
		String txt = fis.readLine();
		while (txt != null) {
			// System.out.println(txt);
			if (txt.startsWith("#define")) {
				String re1 = "(#)"; // Any Single Character 1
				String re2 = "(define)"; // Word 1
				String re3 = "(\\s+)"; // White Space 1
				String re4 = "([a-zA-Z_][a-zA-Z0-9_]*)"; // Variable Name 1
				String re5 = "(\\s+)*"; // White Space 2
				String re6 = "([a-zA-Z_0-9][()a-zA-Z0-9_\"]*)*"; // Variable
																	// Name 2

				Pattern p = Pattern.compile(re1 + re2 + re3 + re4 + re5 + re6,
						Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
				Matcher m = p.matcher(txt);
				while (m.find()) {
					String var1 = m.group(4);
					String var2 = m.group(6);
//					System.out.println("XXX define XXX   "
//							+ String.format("%s ==> %s", var1,
//									var2 == null ? "" : var2));
					map.put(var1, var2 == null ? "" : var2);
				}

			}

			txt = fis.readLine();
		}
		fis.close();
		return map;
	}

	public static class Struct {
		/**
		 * 结构体名称
		 */
		public String name;
		public String namespace;
		/**
		 * 源代码
		 */
		public String source;
		/**
		 * field type and name
		 */
		public Map<String, String> fieldMap = new HashMap<String, String>();
		public String javaSource;
	}

	/**
	 * 分析所有的结构体申明
	 * 
	 * @param txt
	 *            源文件字符串
	 * @return
	 */
	public static List<Struct> getAllStructInfo(String txt) {
		// String ss = txt.replaceAll("(typedef\\s+struct)", "class");
		// return ss = ss.replaceAll("(}\\s*\\w+\\s*;)", "}");
		List<Struct> structList = new ArrayList<StructToClass.Struct>();

		String re1 = "(typedef)"; // Word 1
		String re2 = "(\\s+)"; // White Space 1
		String re3 = "(struct)"; // Word 2
		String re4 = "(\\s+)"; // White Space 2
		String re5 = "((?:[a-z][a-z0-9_]*))"; // Variable Name 1
		String re6 = "(\\s+)"; // White Space 3
		String re7 = "(\\{)"; // Any Single Character 1

		Pattern p = Pattern.compile(re1 + re2 + re3 + re4 + re5 + re6 + re7,
				Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
		Matcher m = p.matcher(txt);
		while (m.find()) {
			// String word1 = m.group(1);
			// String ws1 = m.group(2);
			// String word2 = m.group(3);
			// String ws2 = m.group(4);
			String var1 = m.group(5);
			// String ws3 = m.group(6);
			// String c1 = m.group(7);
//			System.out.println(var1);
//			System.out.println("_______________________");
			Struct struct = new Struct();
			struct.name = var1;
			struct.source = getStrutInfo(struct.name, txt);
			structList.add(struct);
		}
		return structList;
	}

	private static String getStrutInfo(String structName, String txt) {
		String re1 = "(typedef)"; // Word 1
		String re2 = "(\\s+)"; // White Space 1
		String re3 = "(struct)"; // Word 2
		String re4 = "(\\s+)"; // White Space 2

		String re5 = "(" + structName + ")"; // Variable Name 1
		String re6 = "(\\s*\\{)"; // White Space 3
		String re7 = "([\\s\\S]*)"; // Curly Braces 1
		String re8 = "(\\}\\s*" + structName + "\\s*;)"; // White Space 4
		Pattern p = Pattern.compile(re1 + re2 + re3 + re4 + re5 + re6 + re7
				+ re8, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
		Matcher m = p.matcher(txt);
		if (m.find()) {
			return m.group(6) + m.group(7) + m.group(8);
		}
		return "";
	}
}
